package com.qf.spring.framework;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import static java.lang.Class.forName;

/**
 * @author lwc
 * @date 2021/10/25 18:47
 * @Description 模拟 容器类
 */
public class MyClassPathXmlApplicationContext {
    //用于 存放 扫描xml 文件之后 得到的bean的节点信息 beanId为键，BeanDefinition值
    private Map<String,BeanDefination> beansMap = new HashMap<>();

    //用于存放 容器创建出来的对象， beanId为键 反射创建出来的对象为值
    private Map<String, Object> objectMap = new HashMap<>();

    public MyClassPathXmlApplicationContext(){}

    public MyClassPathXmlApplicationContext(String configPath) throws IllegalAccessException, InstantiationException, ClassNotFoundException, InvocationTargetException {

        //1.解析配置文件 把解析的数据封装到 BeanDefinition 然后存放到beansMap
        parseXml(configPath);
        //2.利用反射 创建对象 把创建的对象存放到ObjectMap
        createObject();
       // 3.属性注入 反射调用set方法 给属性注入值 得到完整的bean
        di();

    }

    /**
     * 解析配置文件的方法
     * @param configPath 参数是配置文件所在的路径
     */
    private void parseXml(String configPath) {
        //读取配置文件 得到文件流
        InputStream inputStream = this.getClass().getResourceAsStream(configPath);

        //获取saxReader解析器对象 需要导dom4j包
        SAXReader saxReader = new SAXReader();

        try {
            //Document 文档对象模型 把HTML文档表现为带有元素，属性和文本的树形结构
            Document document = saxReader.read(inputStream);

            //得到根节点
            Element beans = document.getRootElement();

            System.out.println(beans);

            //迭代器  elementIterator()  方法获取是该节点的孩子节点
            Iterator<Element> iterator = beans.elementIterator();
            while (iterator.hasNext()){

                //获取bean节点
                Element bean = iterator.next();
                System.out.println(bean);

                String beanId = bean.attributeValue("id");
                String beanClass = bean.attributeValue("class");

                //封装beanId和beanClass 到 BeanDefinition();
                BeanDefination beanDefination = new BeanDefination();
                beanDefination.setBeanId(beanId);
                beanDefination.setBeanClass(beanClass);

                //获取bean节点的子节点
                Iterator<Element> proIterator = bean.elementIterator();
                while (proIterator.hasNext()){

                    Element pro = proIterator.next();

                    String proName = pro.attributeValue("name");
                    System.out.println(proName);
                    String proRef = pro.attributeValue("ref");

                    //将property节点封装到 PropertyDefination
                    PropertyDefination propertyDefination = new PropertyDefination();
                    propertyDefination.setName(proName);
                    propertyDefination.setRef(proRef);

                    //将propertyDefinition 封装到beanDefination的propsMap中
                    Map<String, PropertyDefination> propsMap = beanDefination.getPropsMap();
                    propsMap.put(proName,propertyDefination);

                }
                beansMap.put(beanId,beanDefination);
            }

        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }
    //创建对象
    private void createObject() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        //遍历上一步得到的beansMap 从中得到class信息 反射创建对象
        Set<Map.Entry<String, BeanDefination>> entrySet = beansMap.entrySet();

        Iterator<Map.Entry<String, BeanDefination>> iterator = entrySet.iterator();

        while (iterator.hasNext()){
            Map.Entry<String, BeanDefination> bean = iterator.next();

            String beanId = bean.getKey();
            BeanDefination beanDefination = bean.getValue();

            //得到类的全路径名
            String beanClass = beanDefination.getBeanClass();
            Object instance = forName(beanClass).newInstance();

            objectMap.put(beanId,instance);
        }
    }

    //属性注入
    private void di() throws ClassNotFoundException, InvocationTargetException, IllegalAccessException {

        Set<Map.Entry<String, BeanDefination>> entries = beansMap.entrySet();
        Iterator<Map.Entry<String, BeanDefination>> iterator = entries.iterator();

        while (iterator.hasNext()) {
            Map.Entry<String, BeanDefination> map = iterator.next();
            String beanId = map.getKey();
            BeanDefination beanDefination = map.getValue();

            //得到bean的property集合
            Map<String, PropertyDefination> propsMap = beanDefination.getPropsMap();

            Set<Map.Entry<String, PropertyDefination>> proSet = propsMap.entrySet();

            Iterator<Map.Entry<String, PropertyDefination>> proIter = proSet.iterator();

            while (proIter.hasNext()) {
                Map.Entry<String, PropertyDefination> mapProperties = proIter.next();

                //得到property的name
                String proName = mapProperties.getKey();
                PropertyDefination propertyDefination = mapProperties.getValue();

                String ref = propertyDefination.getRef();

                //根据property的name属性值 得到对应的set方法
                String setMethodName = "set" + proName.substring(0, 1).toUpperCase() + proName.substring(1);

                String beanClass = beanDefination.getBeanClass();

                Class<?> aClass = forName(beanClass);
                Method[] declaredMethods = aClass.getDeclaredMethods();
                for (Method m : declaredMethods) {

                    //判断类中有没有这个方法
                    if (m.getName().equals(setMethodName)) {

                        /*
                        反射调用set方法 给对象的属性赋值
                        先得到参数对象
                         */
                        Object para = objectMap.get(ref);
                        Object o = objectMap.get(beanId);//当前对象
                        //反射调用方法
                        m.invoke(o, para);

                    }

                }

            }

        }
    }
        public Object getBean(String beanName){
            //
            return objectMap.get(beanName);
    }

}
